package bcontractor.base;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import bcontractor.builder.Builder;

/**
 * Provides an immutable way of creating sets incrementally
 * 
 * @author lundberg
 * 
 * @param <E>
 */
public class IncrementalSet<E> implements Builder<Set<E>> {

    private final Set<E> set;

    public IncrementalSet() {
        this.set = new HashSet<E>();
    }

    private IncrementalSet(Set<E> set) {
        this.set = set;
    }

    public static <E> IncrementalSet<E> aSet() {
        return new IncrementalSet<E>();
    }

    public IncrementalSet<E> with(E element) {
        Set<E> expanded = new HashSet<E>(this.set);
        expanded.add(element);
        return new IncrementalSet<E>(expanded);
    }

    public IncrementalSet<E> with(Collection<? extends E> elements) {
        Set<E> expanded = new HashSet<E>(this.set);
        expanded.addAll(elements);
        return new IncrementalSet<E>(expanded);
    }

    public IncrementalSet<E> without(E element) {
        Set<E> expanded = new HashSet<E>(this.set);
        expanded.remove(element);
        return new IncrementalSet<E>(expanded);
    }

    public IncrementalSet<E> without(Collection<? extends E> elements) {
        Set<E> expanded = new HashSet<E>(this.set);
        expanded.removeAll(elements);
        return new IncrementalSet<E>(expanded);
    }

    public IncrementalSet<E> intersection(Collection<? extends E> elements) {
        Set<E> intersection = new HashSet<E>(this.set);
        intersection.retainAll(elements);
        return new IncrementalSet<E>(intersection);
    }

    public int size() {
        return this.set.size();
    }

    public boolean contains(Object element) {
        return this.set.contains(element);
    }

    public boolean containsAll(Iterable<? extends E> elements) {
        for (E e : elements) {
            if (!this.set.contains(e)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof IncrementalSet<?>)) {
            return false;
        }
        IncrementalSet<?> o = (IncrementalSet<?>) obj;
        return this.set.equals(o.set);
    }

    @Override
    public int hashCode() {
        return this.set.hashCode();
    }

    public Set<E> asSet() {
        return this.build();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Set<E> build() {
        return Collections.unmodifiableSet(this.set);
    }

    /**
     * @return boolean
     */
    public boolean isEmpty() {
        return this.set.isEmpty();
    }
}
